package com.hzw.saas.service.user.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzw.saas.api.user.ICustomerService;
import com.hzw.saas.api.user.IUserAccountService;
import com.hzw.saas.api.user.IUserInfoLogService;
import com.hzw.saas.api.user.IUserProfileService;
import com.hzw.saas.api.user.bo.UserAccountBO;
import com.hzw.saas.api.user.bo.UserAccountProfileBO;
import com.hzw.saas.api.user.bo.UserInfoLogBO;
import com.hzw.saas.api.user.bo.UserProfileBO;
import com.hzw.saas.api.user.enums.UserPropertyEnum;
import com.hzw.saas.api.user.enums.UserStatusEnum;
import com.hzw.saas.api.user.query.*;
import com.hzw.saas.common.config.util.AssertUtil;
import com.hzw.saas.common.config.util.MybatisPlusUtil;
import com.hzw.saas.common.config.util.PageParam;
import com.hzw.saas.common.config.util.PageUtils;
import com.hzw.saas.common.util.MapperUtil;
import com.hzw.saas.common.util.ObjFieldUtil;
import com.hzw.saas.common.util.SnowFlakeUtil;
import com.hzw.saas.common.util.enums.AssertEnum;
import com.hzw.saas.service.user.mapper.UserAccountMapper;
import com.hzw.saas.service.user.model.UserAccount;
import jodd.util.StringUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

/**
 * @author zzl
 * @since 12/21/2020
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class UserAccountServiceImpl extends ServiceImpl<UserAccountMapper, UserAccount> implements IUserAccountService, IService<UserAccount> {

    private final PasswordEncoder passwordEncoder;

    @Resource
    private IUserProfileService userProfileService;
    @Resource(name = "customerServiceImpl")
    private ICustomerService customerService;
    @Resource
    private IUserInfoLogService userInfoLogService;

    @Override
    public boolean existAccount(String userId) {
        if (StringUtils.isBlank(userId)) return false;
        int count = super.lambdaQuery().eq(UserAccount::getUserId, userId).count();
        return !Objects.equals(count, 0);
    }

    @Override
    public void assertExist(String userId, AssertEnum assertEnum) {
        boolean exist = this.existAccount(userId);
        AssertEnum.assertResult(assertEnum, exist, String.format("用户(%s)", userId));
    }

    @Override
    public boolean existAccount(UserAccountGetQuery query) {
        return this.existAccount(query, (String) null);
    }

    @Override
    public void assertExist(UserAccountGetQuery query, AssertEnum assertEnum) {
        boolean exist = this.existAccount(query, (String) null);
        AssertEnum.assertResult(assertEnum, exist, "用户账户");
    }

    @Override
    public boolean existAccount(UserAccountGetQuery query, String excludedUserId) {
        if (ObjFieldUtil.isAllNullFields(query, ObjFieldUtil.ConvertNull.NONE))
            return false;
        int countUser = this.lambdaQuery()
            .ne(UserAccount::getStatus, UserStatusEnum.DELETED.getCode())
            .ne(Objects.nonNull(excludedUserId), UserAccount::getUserId, excludedUserId)
            .and(wrapper -> wrapper
                .or().eq(Objects.nonNull(query.getUserName()), UserAccount::getUserName, query.getUserName())
                .or().eq(Objects.nonNull(query.getPhoneNo()), UserAccount::getPhoneNo, query.getPhoneNo())
                .or().eq(Objects.nonNull(query.getEmail()), UserAccount::getEmail, query.getEmail())).count();
        if (countUser > 0)
            return true;
        // 检查邮箱是否有使用历史
        return Objects.nonNull(query.getEmail())
            && userInfoLogService.existLogExclude(excludedUserId, UserPropertyEnum.EMAIL.property(), query.getEmail());
    }

    @Override
    public void assertExist(UserAccountGetQuery query, String excludedUserId, AssertEnum assertEnum) {
        boolean exist = this.existAccount(query, excludedUserId);
        AssertEnum.assertResult(assertEnum, exist, "用户账户");
    }

    @Override
    public UserAccountBO getAccount(String userId) {
        if (StringUtils.isBlank(userId))
            return null;
        LambdaQueryChainWrapper<UserAccount> query = super.lambdaQuery()
            .eq(UserAccount::getUserId, userId)
            .ne(UserAccount::getStatus, UserStatusEnum.DELETED.getCode());
        MybatisPlusUtil.limitOne(query);
        UserAccount userAccount = query.one();
        return MapperUtil.nf().map(userAccount, UserAccountBO.class);
    }

    @Override
    public UserAccountBO getAccount(UserAccountGetQuery query) {
        if (ObjFieldUtil.isAllNullFields(query, ObjFieldUtil.ConvertNull.BLANK)) {
            return null;
        }
        UserAccount entity = MapperUtil.nf().map(query, UserAccount.class);
        LambdaQueryChainWrapper<UserAccount> lambdaQuery = super.lambdaQuery()
            .ne(UserAccount::getStatus, UserStatusEnum.DELETED.getCode())
            .setEntity(entity);
        MybatisPlusUtil.limitOne(lambdaQuery);
        UserAccount userAccount = lambdaQuery.one();
        return MapperUtil.nf().map(userAccount, UserAccountBO.class);
    }

    @Override
    public List<UserAccountBO> listAccounts(Collection<String> userIds) {
        if (CollUtil.isEmpty(userIds)) {
            return new ArrayList<>();
        }
        List<UserAccount> list = super.lambdaQuery()
            .in(UserAccount::getUserId, userIds)

            .orderByAsc(UserAccount::getRegisteredDate).list();
        return MapperUtil.nf().mapAsList(list, UserAccountBO.class);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserAccountBO addAccount(UserAccountAddQuery account2Add, boolean addCustomer) {
        ObjFieldUtil.isAllNullFields(account2Add, ObjFieldUtil.ConvertNull.BLANK);
        // 检查存在性
        UserAccountGetQuery query = MapperUtil.nf().map(account2Add, UserAccountGetQuery.class);
        this.assertExist(query, AssertEnum.NOT_FOUND);
        // 持久化account
        UserAccount entity = MapperUtil.nf().map(account2Add, UserAccount.class);

        try {
            this.initAddAccount(entity);
            this.save(entity);
            log.debug("user account added: {}", JSONUtil.toJsonStr(entity));
        } finally {
            if (entity.getPassword() != null && entity.getPassword().startsWith("*")) {
                entity.setPassword("*");
            } else {
                entity.setPassword(null);
            }
        }
        account2Add.setUserId(entity.getUserId());
        // userInfoLog
        this.addUserInfoLogs(entity);
        // 持久化profile
        this.addUserProfile(account2Add);
        // 注册是否添加客户信息
        if (addCustomer) {
            this.addCustomer(account2Add);
        }
        return MapperUtil.nf().map(entity, UserAccountBO.class);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateAccount(UserAccountUpdateQuery account2Update, boolean updateCustomer) {
        AssertUtil.assertThrow("userId is blank", StrUtil.isBlank(account2Update.getUserId()));
        ObjFieldUtil.isAllNullFields(account2Update, ObjFieldUtil.ConvertNull.BLANK);
        // 检查存在性
        UserAccount originAccount = super.getById(account2Update.getUserId());
        if (Objects.isNull(originAccount)) return false;
        // 检查存在冲突
        UserAccountGetQuery getQuery = MapperUtil.nf().map(account2Update, UserAccountGetQuery.class);
        this.assertExist(getQuery, account2Update.getUserId(), AssertEnum.NOT_FOUND);
        // 持久化account
        UserAccount newAccount = MapperUtil.nf().map(account2Update, UserAccount.class);
        boolean update = this.updateById(newAccount);
        if (!update)
            return false;
        // 更新profile
        if (StringUtils.isNotBlank(account2Update.getRealName())) {
            UserProfileBO userProfileBO = new UserProfileBO()
                .setUserId(account2Update.getUserId())
                .setRealName(account2Update.getRealName());
            userProfileService.update(userProfileBO);
        }
        // 更新customer
        if (updateCustomer && (Objects.nonNull(account2Update.getPhoneNo()) && !Objects.equals(account2Update.getPhoneNo(), originAccount.getPhoneNo())
            || Objects.nonNull(account2Update.getEmail()) && !Objects.equals(account2Update.getEmail(), originAccount.getEmail()))) {
            CustomerUpdateQuery customerUpdateQuery = new CustomerUpdateQuery();
            customerUpdateQuery.setUserId(account2Update.getUserId())
                .setPhoneNo(account2Update.getPhoneNo())
                .setEmail(account2Update.getEmail());
            customerService.updateCustomer(customerUpdateQuery, null);
        }
        // 增加user info log
        this.addUserInfoLogs(originAccount, newAccount);
        return true;
    }

    @Override
    public boolean resetPassword(String userId, String password) {
        if (StringUtils.isBlank(userId)) {
            return false;
        }
        UserAccount userAccount = new UserAccount();
        userAccount.setUserId(userId).setPassword(passwordEncoder.encode(password));
        int updateNum = this.baseMapper.updateById(userAccount);
        return updateNum > 0;
    }

    @Override
    public UserAccountBO deleteAccount(String userId) {
        UserAccountBO account = this.getAccount(userId);
        if (account == null) {
            return null;
        }
        this.lambdaUpdate()
            .eq(UserAccount::getUserId, userId)
            .set(UserAccount::getStatus, UserStatusEnum.DELETED.getCode())
            .update();
        return account;
    }

    @Override
    @Transactional
    public UserAccountBO deleteAccount(UserAccountGetQuery query) {
        UserAccountBO account = this.getAccount(query);
        if (account == null)
            return null;
        this.lambdaUpdate()
            .eq(UserAccount::getUserId, account.getUserId())
            .set(UserAccount::getStatus, UserStatusEnum.DELETED.getCode())
            .update();
        // TODO: ZZL@05/18/2021 删除用户账户相关的信息
        return account;
    }

    @Override
    public IPage<UserAccountBO> pageAccounts(PageParam pageParam, UserAccountBO query) {
        UserAccount userAccount = MapperUtil.nf().map(query, UserAccount.class);
        Page<UserAccount> userAccountPage = this.baseMapper.selectPage(pageParam.convert(),
            Wrappers.<UserAccount>lambdaQuery()
                .eq(StringUtil.isNotBlank(userAccount.getUserId()), UserAccount::getUserId, userAccount.getUserId())
                .eq(StringUtil.isNotBlank(userAccount.getEmail()), UserAccount::getEmail, userAccount.getEmail())
                .eq(StringUtil.isNotBlank(userAccount.getPhoneNo()), UserAccount::getPhoneNo, userAccount.getPhoneNo())
                .eq(StringUtil.isNotBlank(userAccount.getUserName()), UserAccount::getUserName,
                    userAccount.getUserName())
                .eq(Objects.nonNull(userAccount.getStatus()), UserAccount::getStatus, userAccount.getStatus())
        );
        return PageUtils.convert(userAccountPage, UserAccountBO.class);
    }

    @Override
    public IPage<UserAccountProfileBO> pageAccountsByGroup(PageParam pageParam, UserAccountBO query, List<String> userIds) {
        return this.baseMapper.searchUserByQueryAndGroup(pageParam.convert(), query, userIds);
    }

    @Override
    public List<UserAccountBO> getUserAccounts(List<String> userIds) {
        List<UserAccount> list = this.lambdaQuery().in(UserAccount::getUserId, userIds).list();
        return MapperUtil.nf().mapAsList(list, UserAccountBO.class);
    }

    @Override
    public List<UserAccountBO> findUserInfoWithCondition(List<String> userIds) {
        if (CollectionUtil.isEmpty(userIds)) {
            return new ArrayList<>();
        }
        List<UserAccount> list = this.lambdaQuery()
            .in(UserAccount::getUserId, userIds)
            .orderByDesc(UserAccount::getRegisteredDate).list();
        return MapperUtil.nf().mapAsList(list, UserAccountBO.class);
    }

    @Override
    public UserAccountBO getAccountByPhone(String phoneNum) {
        UserAccountBO account = this.getAccount(new UserAccountGetQuery().setPhoneNo(phoneNum));
        return account == null ? new UserAccountBO() : account;
    }

    @Override
    public UserAccountBO getByEmail(String email) {
        UserAccountBO account = this.getAccount(new UserAccountGetQuery().setEmail(email));
        return account == null ? new UserAccountBO() : account;
    }

//    @Override
//    public List<UserAccountBO> findUserAccount(UserAccountSearchQuery userAccountSearchQuery) {
//        if (StrUtil.isNotBlank(userAccountSearchQuery.getSearchParam())){
//            List<UserAccount> list = this.lambdaQuery().like(UserAccount::getEmail, userAccountSearchQuery.getSearchParam())
//                .or()
//                .like(UserAccount::getPhoneNo, userAccountSearchQuery.getSearchParam())
//                .list();
//            return MapperUtil.nf().mapAsList(list,UserAccountBO.class);
//        }
//        return Collections.emptyList();
//    }

    /**
     * 初始化用户账户数据
     *
     * @param userAccount 账户信息
     */
    private void initAddAccount(UserAccount userAccount) {
        // 密码加密
        userAccount.setPassword(passwordEncoder.encode(userAccount.getPassword()));
        if (StringUtils.isBlank(userAccount.getUserId())) {
            userAccount.setUserId(SnowFlakeUtil.nextIdStr());
        }
        Date now = new Date();
        if (userAccount.getRegisteredDate() == null) {
            userAccount.setRegisteredDate(now);
        }
        // 默认启用用户
        if (userAccount.getStatus() == null) {
            userAccount.setStatus(1);
        }
        if (StrUtil.isNotBlank(userAccount.getPhoneNo())) {
            userAccount.setPhoneVerifyDate(now);
        }
        if (StrUtil.isNotBlank(userAccount.getEmail())) {
            userAccount.setEmailVerifyDate(now);
        }
    }

    /**
     * 用户注册同时增加profile信息
     */
    private void addUserProfile(UserAccountAddQuery userAccountAddQuery) {
        UserProfileBO userProfileBO = MapperUtil.nf().map(userAccountAddQuery, UserProfileBO.class);
        userProfileService.add(userProfileBO);
    }

    /**
     * 根据用户注册信息，添加客户信息
     */
    private void addCustomer(UserAccountAddQuery accountInfo) {
        CustomerAddQuery customerAdd = MapperUtil.nf().map(accountInfo, CustomerAddQuery.class);
        customerAdd.setCustomerNo(accountInfo.getUserName())
            .setCustomerName(accountInfo.getRealName());
        customerService.registerCustomer(customerAdd, null);
    }

    private void addUserInfoLogs(UserAccount entity) {
        List<UserInfoLogBO> userInfoLogs = new ArrayList<>();
        if (StrUtil.isNotBlank(entity.getUserName())) {
            UserInfoLogBO infoLog = new UserInfoLogBO()
                .setUserId(entity.getUserId())
                .setPKey(UserPropertyEnum.USERNAME.property())
                .setPValue(entity.getUserName())
                .setPDesc(UserPropertyEnum.USERNAME.description());
            userInfoLogs.add(infoLog);
        }
        if (StrUtil.isNotBlank(entity.getPhoneNo())) {
            UserInfoLogBO infoLog = new UserInfoLogBO()
                .setUserId(entity.getUserId())
                .setPKey(UserPropertyEnum.PHONE_NO.property())
                .setPValue(entity.getPhoneNo())
                .setPDesc(UserPropertyEnum.PHONE_NO.description());
            userInfoLogs.add(infoLog);
        }
        if (StrUtil.isNotBlank(entity.getEmail())) {
            UserInfoLogBO infoLog = new UserInfoLogBO()
                .setUserId(entity.getUserId())
                .setPKey(UserPropertyEnum.EMAIL.property())
                .setPValue(entity.getEmail())
                .setPDesc(UserPropertyEnum.EMAIL.description());
            userInfoLogs.add(infoLog);
        }
        userInfoLogService.addLogs(userInfoLogs);
    }

    private void addUserInfoLogs(UserAccount originAccount, UserAccount newAccount) {
        UserAccount account2AddLog = new UserAccount();
        account2AddLog.setUserId(originAccount.getUserId());
        if (Objects.nonNull(newAccount.getUserName())
            && !Objects.equals(newAccount.getUserName(), originAccount.getUserName())) {
            account2AddLog.setUserName(newAccount.getUserName());
        }
        if (Objects.nonNull(newAccount.getPhoneNo())
            && !Objects.equals(newAccount.getPhoneNo(), originAccount.getPhoneNo())) {
            account2AddLog.setPhoneNo(newAccount.getPhoneNo());
        }
        if (Objects.nonNull(newAccount.getEmail())
            && !Objects.equals(newAccount.getEmail(), originAccount.getEmail())) {
            account2AddLog.setEmail(newAccount.getEmail());
        }
        this.addUserInfoLogs(account2AddLog);
    }
}
